package com.bio.dao.impl;


import java.io.Serializable;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.FetchMode;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projection;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;

import com.bio.entity.BaseObject;
import com.bio.utils.Pager;
import com.bio.utils.Pagination;

/**
 * Hibernate query tools.
 * The following is the example code:
 * <p>
 *
 *        this.getHibernateTemplate().execute(new HibernateCallback() {
 *            public Object doInHibernate(Session session) throws HibernateException,
 *                    SQLException {
 *                HibernateQuery<MyModel> query = new HibernateQuery<MyModel>(session);
 *                return query.addWhere(Restrictions.eq("id", id))
 *                          .addOrder(Order.asc("id"))
 *                            .query();
 *            }
 *        });
 *
 * </p>
 * @param <T>
 */
public class HibernateQuery<T extends Serializable> implements Serializable {

    private static final long serialVersionUID = 757737707070519057L;

    private Criteria root;
    
    private Criteria count;
    
    private Pagination pagination;
    
    private SessionFactory sessionFactory;
    
    private Session session;
    
//    public HibernateQuery(Session session) {
//        Class clazz = GenricType.get(this.getClass(), 0);
//        System.out.println(clazz);
//        this.root = session.createCriteria(clazz, "this");
//        this.count = session.createCriteria(clazz, "this").setProjection(Projections.rowCount());
//    }
    
//    public HibernateQuery(Session session, Class clazz) {
//    	this.session = session;
//        this.root = session.createCriteria(clazz, "this");
//        this.count = session.createCriteria(clazz, "this").setProjection(Projections.rowCount());
//        
//        if (BaseObject.class.isAssignableFrom(clazz)) {
//        	this.addWhere(Restrictions.eq("deleted", BaseObject.FLAG_UNDELETED));
//        }
//    }

    public HibernateQuery(SessionFactory sessionFactory, Session session, Class clazz) {
    	this.session = session;
    	this.sessionFactory = sessionFactory;
        this.root = session.createCriteria(clazz, "this");
        this.count = session.createCriteria(clazz, "this").setProjection(Projections.rowCount());
        if (BaseObject.class.isAssignableFrom(clazz)) {
        	this.addWhere(Restrictions.eq("deleted", BaseObject.FLAG_UNDELETED));
        }
    }
    
    public HibernateQuery<T> join(String property, String alias) {
        this.root.createAlias(property, alias);
        this.count.createAlias(property, alias);
        
        return this;
    }
    
    public HibernateQuery<T> fetchJoin(String... properties) {
        for (String property : properties) {
            this.root.setFetchMode(property, FetchMode.EAGER);
        }
        
        return this;
    }
    
    public HibernateQuery<T> addWhere(Criterion... criterions) {
        for (Criterion criterion : criterions) {
            this.root.add(criterion);
            this.count.add(criterion);
        }
        
        return this;
    }
    
    public HibernateQuery<T> addOrder(Order... orders) {
        for (Order order : orders) {
            this.root.addOrder(order);
        }
        return this;
    }
    
    public HibernateQuery<T> setPagination(Pagination pagination) {
        this.pagination = pagination;
        
        return this;
    }
    
    /**
     * The count cannot support group
     * @param projection
     * @return
     */
    public HibernateQuery<T> group(Projection projection) {
        this.root.setProjection(projection);
        
        return this;
    }
    
    public Pager<T> query() {
    	try {
	        if (this.pagination != null) {
	            //Get the number of rows.
	            Long l_total = (Long) this.count.list().get(0);
	            String s_total = String.valueOf(l_total);
	            this.pagination.setTotal(Integer.valueOf(s_total));
	            this.root.setFirstResult(this.pagination.getCurrentIndex());
	            this.root.setMaxResults(this.pagination.getMax());
	        }
	        
	        List<T> list = this.root.list();
	        
	        // If no the specified pagination, then the total and max equal the size of the list.
	        if (this.pagination == null) {
	            this.pagination = new Pagination(list.size());
	            this.pagination.setTotal(list.size());
	        }
	        Pager<T> pager = new Pager<T>(this.pagination, list);
	        
	        return pager;
    	} finally {
    		//SessionFactoryUtils.releaseSession(this.session, this.sessionFactory);
    	}
    }
 
}
